import HNode from "../../type/HNode";
import {emptyNode, HTML_NODE_TYPE} from "../../constant";
import {
    parseProperty
} from './parse';
import {
    processElement,
    processText,
    processComment,
    resetGlobalDirectiveBit
} from './process';
import {isDef} from "../../shared/util";

export class ASTParser {

    static inPre = false;
    static preStateStack = new Array();
    static inFor = false;
    static forStateStack = new Array();
    static inOnce = false;
    static onceStateStack = new Array();

    static isComponentTemplate = false;

    /**
     * 初始化 AST 解析器 状态标志位
     * @param {Boolean} isComponentTemplate
     */
    static initGlobalState(isComponentTemplate) {
        ASTParser.inPre = false;
        ASTParser.preStateStack.length = 0;
        ASTParser.inFor = false;
        ASTParser.forStateStack.length = 0;
        ASTParser.inOnce = false;
        ASTParser.onceStateStack.length = 0;

        ASTParser.isComponentTemplate = isComponentTemplate;
    }

    /**
     * 分析 Html 节点树，并将 html节点树
     * 转化为 AST 节点树（并且会将HTML节点
     * 中的动态指令翻译为对应的AST节点结构）
     * @param {HNode} root
     * @param {Boolean} isComponentTemplate
     */
    static parse(root, isComponentTemplate) {
        // 初始化全局标志变量
        ASTParser.initGlobalState(isComponentTemplate);

        // 入口条件检查
        if (!(root instanceof HNode)) {
            throw new Error("root 参数不是 HNode 类型，请检查！");
        }
        // 如果是空节点
        if (root === emptyNode) {
            return emptyNode;
        }

        // 将 HTML Node 转换为 AST Node
        const astRoot = ASTParser.convertHtmlToAst(root);
        // 返回 AST 树的根节点
        return astRoot;
    }

    /**
     * 将HTML节点转换为AST节点
     * 采用深度遍历的方式
     * @param {HNode|ASTNode} hNode
     * @return {ASTNode}
     */
    static convertHtmlToAst(hNode) {

        /**
         * 这里防止处理一些在 AST 解析过程中产生的已经处理好的 AST 节点
         * 比如在处理插值表达式时，产生的新的文本节点
         */

        // 如果节点未处理 或 不含有 elementProcessed 属性
        if (!hNode.elementProcessed) {

            // 扩展 语法处理是否结束 标记位
            hNode.elementProcessed = false;
            // 扩展 是否被标记该节点需要删除
            hNode.needDelete = false;
            // 扩展 HNode 的属性
            hNode.isDynamic = false;
            // 扩展各种指令情况标记
            hNode.conditionBitMap = {
                pre: false,
                slotDeclaration: false,
                slotImplement: false,
                once: false,
                for: false,
                key: false,
                show: false,
                if: false,
                elseIf: false,
                else: false,
                component: false,
            };
            // 各种 指令未处理的字符串值
            hNode.conditionStrValueMap = {};
            // 扩展指令信息存储结构
            hNode.conditionMessage = {};

            // Codegen 层所使用的 语法处理标记
            hNode.processedBitMap = {
                pre: false,
                slotDeclaration: false,
                slotImplement: false,
                once: false,
                for: false,
                key: false,
                show: false,
                if: false,
                component: false,
            };
            // Codegen 层使用的 代码生成标记
            hNode.generated = false;

            // 注释节点处理
            if (hNode.type === HTML_NODE_TYPE.COMMENT) {
                // 对注释节点进行处理
                processComment(hNode);
                // 处理结束返回
                return hNode;
            }

            // 文本节点处理
            if (hNode.type === HTML_NODE_TYPE.TEXT) {
                // 对可能存在的插值表达式进行解析
                processText(hNode);
                // 处理结束后返回
                return hNode;
            }

            // 普通节点
            if (hNode.type === HTML_NODE_TYPE.TAG) {
                // 处理动态属性
                parseProperty(hNode);
                // fixme 特殊信息打标
                // fixme 因为有些插槽节点上没有使用 v-slot 语法
                // fixme 所以需要在 AST 解析时，进行打标
                if (
                    isDef(hNode.parent)
                    && isDef(hNode.parent.conditionBitMap)
                    && hNode.parent.conditionBitMap.component
                ) {
                    hNode.conditionBitMap.slotImplement = true;
                }
                // 对指令信息进行解析
                processElement(hNode);
            }

            // 进行递归深度遍历
            for (let i = 0; i < hNode.children.length; i++) {
                const child = hNode.children[i];
                // 处理节点
                this.convertHtmlToAst(child);
                // 如果节点需要被删除，则将该节点从孩子数组中删除，并将迭代器减一
                if (child.needDelete) {
                    hNode.children.splice(i, 1);
                    i--;
                }
            }

            // 复位全局指令标志
            resetGlobalDirectiveBit(hNode);
        }

        // 返回处理好的 HNode 节点（返回时已经是 AST 节点）
        return hNode;
    }

}
